package com.anlu.base;

import com.anlu.base.database.DbManager;
import lombok.extern.slf4j.Slf4j;

import javax.swing.text.html.Option;
import java.util.Optional;

@Slf4j
public class AppManager {

    /**
     * Caching Policy.
     */
    private CachingPolicy cachingPolicy;
    /**
     * Database Manager.
     */
    private final DbManager dbManager;
    /**
     * Cache Store.
     */
    private final CacheStone cacheStore;

    /**
     * Constructor.
     *
     * @param newDbManager database manager
     */
    public AppManager(final DbManager newDbManager) {
        this.dbManager = newDbManager;
        this.cacheStore = new CacheStone(newDbManager);
    }


    public void initDb(){
         dbManager.connect();
    }

    /**
     * Returns String.
     *
     * @return String
     */
    public String printCacheContent() {
        return cacheStore.print();
    }

    public void initCachingPolicy(final  CachingPolicy policy){
        cachingPolicy = policy;
        if(cachingPolicy == CachingPolicy.BEHIND){
            Runtime.getRuntime().addShutdownHook(new Thread(cacheStore::flushCache));
        }
        cacheStore.clearCache();
    }

    /**
     * Find user account.
     *
     * @param userId String
     * @return {@link UserAccount}
     */
    public UserAccount find(final String userId) {
        log.info("Trying to find {} in cache", userId);
        if (cachingPolicy == CachingPolicy.THROUGH
                || cachingPolicy == CachingPolicy.AROUND) {
            return cacheStore.readThrough(userId);
        } else if (cachingPolicy == CachingPolicy.BEHIND) {
            return cacheStore.readThroughWithWriteBackPolicy(userId);
        } else if (cachingPolicy == CachingPolicy.ASIDE) {
            return findAside(userId);
        }
        return null;
    }

    /**
     * Cache-Aside save user account helper.
     *
     * @param userAccount {@link UserAccount}
     */
    private void saveAside(final UserAccount userAccount) {
        dbManager.updateDb(userAccount);
        cacheStore.invalidate(userAccount.getUserID());
    }

    private UserAccount findAside(String userId) {
        return Optional.ofNullable(cacheStore.get(userId))
                .or(()->{
                    Optional<UserAccount> userAccount =Optional.ofNullable(dbManager.readFromDb(userId));
                    userAccount.ifPresent(account -> cacheStore.set(userId, account));
                    return userAccount;
                }).orElse(null);
    }

    /**
     * Save user account.
     *
     * @param userAccount {@link UserAccount}
     */
    public void save(final UserAccount userAccount) {
        log.info("Save record!");
        if (cachingPolicy == CachingPolicy.THROUGH) {
            cacheStore.writeThrough(userAccount);
        } else if (cachingPolicy == CachingPolicy.AROUND) {
            cacheStore.writeAround(userAccount);
        } else if (cachingPolicy == CachingPolicy.BEHIND) {
            cacheStore.writeBehind(userAccount);
        } else if (cachingPolicy == CachingPolicy.ASIDE) {
            saveAside(userAccount);
        }
    }

}
